#include "lighting.h"
#include <set>
#include <iostream>
#include <algorithm>
#include <GL/gl.h>
#include "light_spot.h"

Lighting::Lighting(){
	disabled=false;
	glGetIntegerv(GL_MAX_LIGHTS,&max_hw_lights);
}

Lighting::~Lighting(){
	for(unsigned int c=0;c<_lights.size();c++){
		delete _lights[c];
	}
}

Lighting& Lighting::instance(){
	static Lighting _instance;
	return _instance;
}

Light* Lighting::createLight(const Point& pos,RGBAColor diffuse,RGBAColor ambient,float cA,float lA,float qA){
	_lights.push_back(new Light(pos,diffuse,ambient,cA,lA,qA));
	return _lights.back();
}

Light* Lighting::createSpotLight(const Point& pos,RGBAColor diffuse,RGBAColor ambient,Point dir,float cutoff,float cA,float lA,float qA){
	_lights.push_back(new LightSpot(pos,diffuse,ambient,dir,cutoff,cA,lA,qA));
	return _lights.back();
}

void Lighting::removeLight(Light* light){
	_lights.erase(std::find(_lights.begin(),_lights.end(),light));
}

std::vector<Light*>::const_iterator Lighting::lights(){
	return _lights.begin();
}

std::vector<Light*>::const_iterator Lighting::lights_end(){
	return _lights.end();
}

void Lighting::disableLights(){
	for(unsigned int c=0;c<_lights.size();c++){
		if(_lights[c]->enabled())
			_lights[c]->off();
	}
}

bool Lighting::enableLights(std::vector<Light*>::const_iterator& prev){
	bool somethingEnabled=false;
	unsigned int j=0;

	for(std::vector<Light*>::const_iterator it=_lights.begin();it!=prev;++it)
		(*it)->off();

	for(int c=0;c<max_hw_lights && prev!=_lights.end();++prev,c++){
		if((*prev)->enabled()){
			(*prev)->off();
			(*prev)->on(j);
			j++;
			somethingEnabled=true;
		}
	}
	return somethingEnabled;
}

void Lighting::enableAllLights(){
	int j=0;
	for(int c=0;c<(int)_lights.size() && j<max_hw_lights;c++){
		if(_lights[c]->enabled()){
			_lights[c]->off();
			_lights[c]->on(j);
			j++;
		}
	}
}

void Lighting::enableLights(const Point& eye,const Point& object){
	if(disabled)
		return;

	std::set<Light*,LightCompare> sorted;
	std::set<Light*,LightCompare>::reverse_iterator it;

	int hw_light=0;
	for (unsigned int i = 0; i < _lights.size() && hw_light<max_hw_lights; i++) {
		if(_lights[i]->enabled()){
			// esta luz esta en el infinito directamente la enciendo
			if(_lights[i]->position().x4!=0){
				/* Calculate object to light position vector. */
				Point objectToLight = (_lights[i]->position()-object);

				//		   2
				// || x ||^
				float quadraticDistance = objectToLight*objectToLight;
				/* True distance needed, take square root. */
				float distance = sqrt(quadraticDistance);

				/* Lambertian surface-based brightness determination. */
				/* Calculated normalized object to eye position direction (nx,ny,nz). */
				Point objectToEye = (eye-object);
				objectToEye *= (1/objectToEye.mod());

				/* Calculate normalized object to light postition direction (dx,dy,dz).
				 */
				objectToLight *= 1/objectToLight.mod();

				float diffuseReflection = objectToEye*objectToLight;
				if(diffuseReflection<0)
					diffuseReflection=0;

				/* Attenuate based on linear distance. */
				_lights[i]->distance(distance);

				float b = floor(diffuseReflection/(_lights[i]->constantAttenuation() + _lights[i]->linearAttenuation()*distance + _lights[i]->quadraticAttenuation()*quadraticDistance) + 0.00005f);
				_lights[i]->brightness(b);
				_lights[i]->off();

				sorted.insert(_lights[i]);
			}else{
				_lights[i]->on(hw_light);
				hw_light++;
			}
		}
	}

	it=sorted.rbegin();
	for(;hw_light<max_hw_lights && it!=sorted.rend();++it){
		(*it)->on(hw_light);
		hw_light++;
	}
}
